using System;
using System.Net;
using System.IO;
using System.Web;
using System.Web.UI;
using System.Collections;
using System.Text;
using System.Configuration;
using com.flajaxian.AWS;
using System.Security;

namespace com.flajaxian.AWS
{
    /// <summary>
    /// This is adapter for the com.flajaxian.FileUploader control. 
    /// It is designed to upload files to an Amazon Web Service.
    /// </summary>
    public class AmazonUploader : FileUploaderAdapter
    {
        private AccessControlCollection _accessControlList;
        private int _timeout = AmazonUploader.DefaultTimeout;
        private string _accessKey;
        private string _secretKey;
        private string _bucketName;
        private string _path;

        /// <summary>
        /// This is an event fired in the moment when the response from the web service is received
        /// </summary>
        public event EventHandler<ResponseReceivedEventArgs> ResponseReceived;
        /// <summary>
        /// Event allowing to override file name
        /// </summary>
        public event EventHandler<FileNameDeterminingEventArgs> FileNameDetermining;
        /// <summary>
        /// Event allowing to modify file stream
        /// </summary>
        public event EventHandler<PreSendingFileStreamEventArgs> PreSendingFileStream;
        /// <summary>
        /// This is a constant for the prefix used for the
        /// <b>AccessKey</b>, <b>SecretKey</b> or <b>BucketName</b> properties 
        /// to indicate that the property shows not a literal value but 
        /// just a key for a WebConfig appSetting value that holds the actual value. (read only)
        /// </summary>
        public const string WebConfigPrefix = "WebConfig:";
        /// <summary>
        /// The default request timeout is 10 minutes. (read only, if you want to change it see Timeout property)
        /// </summary>
        public const int DefaultTimeout = 600000;// in milliseconds

        /// <summary>
        /// This is a method called by the com.flajaxian.FileUploader control
        /// when processing the request to the web service
        /// </summary>
        /// <param name="file">HttpPostedFile object comming from the HttpRequest object</param>
        public override void ProcessFile(HttpPostedFile file)
        {
            string fileName = this.OnFileNameDetermining(file);

            if(fileName != null)
            {
                Stream fileStream = this.OnPreSendingFileStream(file);

                if(fileStream != null)
                {
                    AmazonPutObjectService apos = new AmazonPutObjectService();

                    apos.BucketName = this.BucketName;
                    apos.AccessKey = this.AccessKey;
                    apos.SecretKey = this.SecretKey;
                    apos.Path = this.Path;
                    apos.AccessControlList = this.AccessControlList;

                    HttpWebResponse response = apos.CallService(fileStream, fileName);

                    this.OnResponseReceived(new ResponseReceivedEventArgs(response));
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="args"></param>
        protected virtual void OnResponseReceived(ResponseReceivedEventArgs args)
        {
            if (this.ResponseReceived != null)
            {
                this.ResponseReceived(this, args);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="file"></param>
        protected virtual string OnFileNameDetermining(HttpPostedFile file)
        {
            if (this.FileNameDetermining != null)
            {
                FileNameDeterminingEventArgs args = new FileNameDeterminingEventArgs(file);

                this.FileNameDetermining(this, args);

                if (args.CancelProcessing)
                {
                    return null;
                }

                return args.FileName;
            }
            return file.FileName;
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        protected virtual Stream OnPreSendingFileStream(HttpPostedFile file)
        {
            if (this.PreSendingFileStream != null)
            {
                PreSendingFileStreamEventArgs args = new PreSendingFileStreamEventArgs(file);

                this.PreSendingFileStream(this, args);

                return args.FileStream;
            }
            return file.InputStream;
        }

        /// <summary>
        /// List of Access control object that can be used to define the permissions for the uploaded files
        /// </summary>
        [PersistenceMode(PersistenceMode.InnerProperty)]
        public AccessControlCollection AccessControlList
        {
            get 
            {
                if (this._accessControlList == null)
                {
                    this._accessControlList = new AccessControlCollection();
                }
                return this._accessControlList; 
            }
        }

        /// <summary>
        /// Request timeout in milliseconds
        /// </summary>
        public int Timeout
        {
            get { return this._timeout; }
            set { this._timeout = value; }
        }


        /// <summary>
        /// Access key for the amazon web service. There are two ways for useing this property.
        /// First you can just enter the access key or you can use WebConfig: prefix and then enter 
        /// the appSetting Web.Config key that holds the actual value
        /// </summary>
        public string AccessKey
        {
            get 
            {
                return this.CheckForWebConfigEntry(this._accessKey, SR.NoAccessKeyWasSet, SR.AccessKey);
            }
            set { this._accessKey = value; }
        }

        /// <summary>
        /// Secret key for the amazon web service. There are two ways for useing this property.
        /// First you can just enter the secret key or you can use WebConfig: prefix and then enter 
        /// the appSetting Web.Config key that holds the actual value
        /// </summary>
        public string SecretKey
        {
            get 
            {
                return this.CheckForWebConfigEntry(this._secretKey, SR.NoSecretKeyWasSet, SR.SecretKey);
            }
            set { this._secretKey = value; }
        }

        /// <summary>
        /// Bucket name for the amazon web service. There are two ways for useing this property.
        /// First you can just enter the bucket name or you can use WebConfig: prefix and then enter 
        /// the appSetting Web.Config key that holds the actual value
        /// </summary>
        public string BucketName
        {
            get
            {
                return this.CheckForWebConfigEntry(this._bucketName, SR.NoBucketNameWasSet, SR.BucketName);
            }
            set { this._bucketName = value; }
        }

        /// <summary>
        /// The folder path
        /// </summary>
        public string Path
        {
            get { return this._path; }
            set { this._path = value; }
        }

        private void CheckParameter(string parameter, string errorMessage, string parameterName)
        {
            if (String.IsNullOrEmpty(parameter))
            {
                throw new ArgumentException(errorMessage, parameterName);
            }
        }

        private string CheckForWebConfigEntry(string code, string errorMessage, string parameterName)
        {
            this.CheckParameter(code, errorMessage, parameterName);
            if (code.StartsWith(AmazonUploader.WebConfigPrefix))
            {
                code = ConfigurationManager.AppSettings[System.Text.RegularExpressions.Regex.Split(code, AmazonUploader.WebConfigPrefix)[1]];
                this.CheckParameter(code, errorMessage, parameterName);
            }
            return code;
        }
    }

}
